#pragma once
#include <iostream>
#include <mutex>
#include <mysql/mysql.h>
#include <jsoncpp/json/json.h>
#include "log.hpp"

const char *host = "8.210.143.192";
const char *user = "zwz";
const char *passwd = "zwz04";
const char *db = "gobang";
const unsigned int port = 3306;

class Mysql
{
protected:
    MYSQL *_mysql;

public:
    Mysql()
    {
        _mysql = mysql_init(nullptr);
        _mysql = mysql_real_connect(_mysql, host, user, passwd, db, port, nullptr, 0);
        if (_mysql == nullptr)
        {
            Log(ERROR, "mysql open failed");
            return;
        }
        if (mysql_set_character_set(_mysql, "utf8") != 0)
        {
            Log(ERROR, "mysql set character failed");
            return;
        }
    }
    virtual ~Mysql()
    {
        mysql_close(_mysql);
    }
};

class MysqlUserTable : public Mysql
{
private:
    std::mutex _mutex;

public:
    MysqlUserTable()
    {
    }
    ~MysqlUserTable()
    {
    }
    // 新增用户
    bool insert(Json::Value &val)
    {
        if (val["name"].isNull() || val["password"].isNull())
        {
            Log(WARNING, "Name or password is NULL");
            return false;
        }
#define MYSQL_USER_INSERT "insert into user values (null, '%s', password('%s'), 1000, 0, 0)"
        char buffer[1024];
        sprintf(buffer, MYSQL_USER_INSERT, val["name"].asCString(), val["password"].asCString());
        if (mysql_query(_mysql, buffer) != 0)
        {
            Log(WARNING, "mysql insert into user failed");
            return false;
        }
        Log(INFO, "user:%s is inserted to mysql", val["name"].asCString());
        return true;
    }

    bool selectAndRead(const char *buffer, Json::Value &val)
    {
        MYSQL_RES *res = nullptr;
        // 同时存在读写需要加锁
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (mysql_query(_mysql, buffer) != 0)
            {
                Log(WARNING, "mysql insert into user failed");
                return false;
            }
            res = mysql_store_result(_mysql);
            // 如果没有返回值说明无用户
            if (res == nullptr)
            {
                Log(WARNING, "mysql no user info");
                mysql_free_result(res);
                return false;
            }
        }
        int row = mysql_num_rows(res);
        // 如果找不到
        if (row != 1)
        {
            Log(WARNING, "mysql no user info or error");
            mysql_free_result(res);
            return false;
        }
        MYSQL_ROW rows = mysql_fetch_row(res);
        val["id"] = atoi(rows[0]);
        val["score"] = atoi(rows[1]);
        val["total_count"] = atoi(rows[2]);
        val["win_count"] = atoi(rows[3]);

        // 需要及时释放res
        mysql_free_result(res);
        return true;
    }
    // 登陆并且添加用户信息
    bool login(Json::Value &val)
    {
        if (val["name"].isNull())
        {
            Log(WARNING, "Name or password is NULL");
            return false;
        }
#define MYSQL_USER_LOGIN "select id,score,total_count,win_count from user where name='%s' and password=password('%s')"
        char buffer[1024];
        sprintf(buffer, MYSQL_USER_LOGIN, val["name"].asCString(), val["password"].asCString());

        if (selectAndRead(buffer, val) == false)
            return false;

        return true;
    }
    // 通过用户名获取详细信息
    bool getByName(const std::string &name, Json::Value &val)
    {
#define MYSQL_GET_NAME "select id,score,total_count,win_count from user where name='%s'"
        char buffer[1024];
        sprintf(buffer, MYSQL_GET_NAME, name.c_str());

        if (selectAndRead(buffer, val) == false)
            return false;
        val["name"] = name;
        return true;
    }
    // 通过id获取详细信息
    bool getById(const uint32_t id, Json::Value &val)
    {
#define MYSQL_GET_ID "select id,score,total_count,win_count,name from user where id=%d"
        char buffer[1024];
        sprintf(buffer, MYSQL_GET_ID, id);

        MYSQL_RES *res = nullptr;
        // 同时存在读写需要加锁
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (mysql_query(_mysql, buffer) != 0)
            {
                Log(WARNING, "mysql select into user failed");
                return false;
            }
            res = mysql_store_result(_mysql);
            // 如果没有返回值说明无用户
            if (res == nullptr)
            {
                Log(WARNING, "mysql no user info");
                mysql_free_result(res);
                return false;
            }
        }
        int row = mysql_num_rows(res);
        // 如果找不到
        if (row != 1)
        {
            Log(WARNING, "mysql no user info or error");
            mysql_free_result(res);
            return false;
        }
        MYSQL_ROW rows = mysql_fetch_row(res);
        val["id"] = atoi(rows[0]);
        val["score"] = atoi(rows[1]);
        val["total_count"] = atoi(rows[2]);
        val["win_count"] = atoi(rows[3]);
        val["name"] = rows[4];

        // 需要及时释放res
        mysql_free_result(res);
        return true;
    }
    // 胜利更新 score+30 total_count+1 win_count+1
    bool win(uint32_t id)
    {
#define MYSQL_USER_WIN "update user set score=score+30,total_count=total_count+1,win_count=win_count+1 where id=%d"
        char buffer[1024];
        sprintf(buffer, MYSQL_USER_WIN, id);
        if (mysql_query(_mysql, buffer) != 0)
        {
            Log(WARNING, "mysql insert into user failed");
            return false;
        }
        return true;
    }
    // 失败更新 score-30 total_count+1
    bool lose(uint32_t id)
    {
#define MYSQL_USER_LOSE "update user set score=score-30,total_count=total_count+1 where id=%d"
        char buffer[1024];
        sprintf(buffer, MYSQL_USER_LOSE, id);
        if (mysql_query(_mysql, buffer) != 0)
        {
            Log(WARNING, "mysql insert into user failed");
            return false;
        }
        return true;
    }
};